#include "Config.hpp"
#include "Camera.hpp"
#include "CameraApi.h"
#include "CameraStatus.h"
#include <any>
#include <array>
#include <functional>
#include <spdlog/spdlog.h>
#include <vector>

#define CHECK_CAMERA_API(apicall)                                              \
  if (apicall != CAMERA_STATUS_SUCCESS)                                        \
    return false;

struct ConfigItem {
  BaseConfig::item item;
  std::function<bool(int handle, std::any &value)> funcGet;
  std::function<bool(int handle, std::any value)> funcSet;
};

const static std::array<ConfigItem, 3> configList = {
    ConfigItem{
        .item =
            {.id = 0,
             .describe =
                 R"JSON( {"name":"图像宽度","range":{"min":0,"max":2000}} )JSON",
             .type = BaseConfig::item_type::I_LONG,
             .readonly = false},
        .funcGet = [](int handle, std::any &item) -> bool {
          tSdkImageResolution resoluton;
          CHECK_CAMERA_API(CameraGetImageResolution(handle, &resoluton))
          item = resoluton.iWidth;
          return true;
        },
        .funcSet = [](int handle, std::any item) -> bool {
          if (item.type() != typeid(long))
            return false;
          tSdkImageResolution resoluton;
          CHECK_CAMERA_API(CameraGetImageResolution(handle, &resoluton))
          resoluton.iWidth = std::any_cast<long>(item);
          CHECK_CAMERA_API(CameraSetImageResolution(handle, &resoluton));
          return true;
        }},
    ConfigItem{
        .item =
            {.id = 1,
             .describe =
                 R"JSON( {"name":"图像高度","range":{"min":0,"max":2000}} )JSON",
             .type = BaseConfig::item_type::I_LONG,
             .readonly = false},
        .funcGet = [](int handle, std::any &item) -> bool {
          tSdkImageResolution resoluton;
          CHECK_CAMERA_API(CameraGetImageResolution(handle, &resoluton))
          item = resoluton.iHeight;
          return true;
        },
        .funcSet = [](int handle, std::any item) -> bool {
          if (item.type() != typeid(long))
            return false;
          tSdkImageResolution resoluton;
          CHECK_CAMERA_API(CameraGetImageResolution(handle, &resoluton))
          resoluton.iHeight = std::any_cast<long>(item);
          CHECK_CAMERA_API(CameraSetImageResolution(handle, &resoluton));
          return true;
        }},
    ConfigItem{
        .item =
            {.id = 2,
             .describe =
                 R"JSON( {"name":"曝光时间","range":{"min":0,"max":2000}} )JSON",
             .type = BaseConfig::item_type::I_DOUBLE,
             .readonly = false},
        .funcGet = [](int handle, std::any &item) -> bool {
          double exposure;
          CHECK_CAMERA_API(CameraGetExposureTime(handle, &exposure));
          item = exposure;
          return true;
        },
        .funcSet = [](int handle, std::any item) -> bool {
          if (item.type() != typeid(double))
            return false;
          double val = std::any_cast<double>(item);
          CHECK_CAMERA_API(CameraSetExposureTime(handle, val));
          return true;
        }},
};

#undef CHECK_CAMERA_API

MindVisionConfig::MindVisionConfig(int _handle) : handle(_handle) {}

std::vector<BaseConfig::item> MindVisionConfig::ItemList() {
  std::vector<BaseConfig::item> res;
  res.reserve(configList.size());
  for (auto &i : configList)
    res.push_back(i.item);
  return res;
}

#define CONFIG_CHECK_INPUT_PARA(TYPE)                                          \
  if (id >= configList.size()) {                                               \
    spdlog::warn("MindVisionConfig Get " #TYPE " ID too large");               \
    return false;                                                              \
  }                                                                            \
  if (configList[id].item.type != BaseConfig::item_type::TYPE) {               \
    spdlog::warn("MindVisionConfig Get " #TYPE " type err");                   \
    return false;                                                              \
  }

bool MindVisionConfig::Get(int id, std::string &value) {
  CONFIG_CHECK_INPUT_PARA(I_STRING)
  if (id >= configList.size()) {
    spdlog::warn("MindVisionConfig Get string ID too large");
    return false;
  }
  if (configList[id].item.type != BaseConfig::item_type::I_STRING) {
    spdlog::warn("MindVisionConfig Get string type err");
    return false;
  }
  std::any val = std::string();
  bool ret = configList[id].funcGet(handle, val);
  value = std::any_cast<std::string>(val);
  return ret;
}

bool MindVisionConfig::Get(int id, long &value) {
  CONFIG_CHECK_INPUT_PARA(I_LONG)
  std::any val = long(0);
  bool ret = configList[id].funcGet(handle, val);
  value = std::any_cast<long>(val);
  return ret;
}

bool MindVisionConfig::Get(int id, double &value) {
  CONFIG_CHECK_INPUT_PARA(I_DOUBLE)
  std::any val = double(0);
  bool ret = configList[id].funcGet(handle, val);
  value = std::any_cast<double>(val);
  return ret;
}

bool MindVisionConfig::Get(int id, bool &value) {
  CONFIG_CHECK_INPUT_PARA(I_BOOL)
  std::any val = true;
  bool ret = configList[id].funcGet(handle, val);
  value = std::any_cast<bool>(val);
  return ret;
}

bool MindVisionConfig::Set(int id, const std::string &value) {
  CONFIG_CHECK_INPUT_PARA(I_STRING)
  return configList[id].funcSet(handle, std::any(value));
}

bool MindVisionConfig::Set(int id, const long &value) {
  CONFIG_CHECK_INPUT_PARA(I_LONG)
  return configList[id].funcSet(handle, std::any(value));
}

bool MindVisionConfig::Set(int id, const double &value) {
  CONFIG_CHECK_INPUT_PARA(I_DOUBLE)
  return configList[id].funcSet(handle, std::any(value));
}

bool MindVisionConfig::Set(int id, const bool &value) {
  CONFIG_CHECK_INPUT_PARA(I_BOOL)
  return configList[id].funcSet(handle, std::any(value));
}

#undef CONFIG_CHECK_INPUT_PARA
